/** 
 *
 * Lisense:
 * Copyright (c) 2015.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * @fileDesc:TODO
 * @author:dairymix
 * @version:1.0.0
 */
package com.serv.config.datasource.dynamic;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.YamlMapFactoryBean;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.bind.YamlConfigurationFactory;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.type.AnnotationMetadata;

import com.google.common.collect.Maps;

/**
 * 动态数据源注册
 *
 * @author 单红宇(365384722)
 * @myblog http://blog.csdn.net/catoop/
 * @create 2016年1月24日
 */
//@EnableConfigurationProperties({BootDataSourceConfig.class,ActivitiDataSourceConfig.class})
public class DynamicDataSourceRegister
        implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class);

    //@Autowired
    //private BootDataSourceConfig boot = getYamlConfig(BootDataSourceConfig.class, DATASOURCE_RESOURCE);
    //@Autowired
    //private ActivitiDataSourceConfig activiti = getYamlConfig(ActivitiDataSourceConfig.class, DATASOURCE_RESOURCE);

    private static final String DATASOURCE_RESOURCE = "ds/datasource.yml";
    
    // 如配置文件中未指定数据源类型，使用该默认值
    private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource";
    // private static final Object DATASOURCE_TYPE_DEFAULT =
    // "com.zaxxer.hikari.HikariDataSource";

    // 数据源
    private DataSource defaultDataSource;
    private Map<String, Object> customDataSources = new HashMap<String, Object>();

    public Properties getYamlProp(String resource){
        YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
        yaml.setResources(new ClassPathResource(resource));
        return yaml.getObject();
    }

    public Map<String, Object> getYamlMap(String resource){
        YamlMapFactoryBean yaml = new YamlMapFactoryBean();
        yaml.setResources(new ClassPathResource(resource));
        return yaml.getObject();
    }

    public <T> T getYamlConfig(Class<T> clazz,String resource){
        try {
            YamlConfigurationFactory<T> yaml = new YamlConfigurationFactory<T>(clazz);
            //yaml.setYaml(resource);
            yaml.setResource(new ClassPathResource(resource));
            return (T) yaml.getObject();
        } catch (Exception e) {
            logger.error("can not get yaml config");
            e.printStackTrace();
            return null;
        }
    }

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Map<String, Object> targetDataSources = new HashMap<String, Object>();
        // 将主数据源添加到更多数据源中
        targetDataSources.put("dataSource", defaultDataSource);
        DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
        // 添加更多数据源
        targetDataSources.putAll(customDataSources);
        for (String key : customDataSources.keySet()) {
            DynamicDataSourceContextHolder.dataSourceIds.add(key);
        }

        // 创建DynamicDataSource
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(DynamicDataSource.class);
        beanDefinition.setSynthetic(true);
        MutablePropertyValues mpv = beanDefinition.getPropertyValues();
        mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
        mpv.addPropertyValue("targetDataSources", targetDataSources);
        registry.registerBeanDefinition("dataSource", beanDefinition);

        logger.info("Dynamic DataSource Registry");
    }

    /**
     * 创建DataSource
     *
     * @param type
     * @param driverClassName
     * @param url
     * @param username
     * @param password
     * @return
     * @author SHANHY
     * @create 2016年1月24日
     */
    @SuppressWarnings("unchecked")
    public DataSource buildDataSource(Map<String, Object> dsMap) {
        try {
            String url = dsMap.get("url").toString();
            String username = dsMap.get("username").toString();
            String password = dsMap.get("password").toString();
            String driverClassName = dsMap.get("driverClassName").toString();

            Object type = dsMap.get("type");
            if (type == null)
                type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource

            Class<? extends DataSource> dataSourceType;
            dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);

            DataSourceBuilder factory = 
                    DataSourceBuilder
                    .create()
                    .driverClassName(driverClassName)
                    .url(url)
                    .username(username)
                    .password(password)
                    .type(dataSourceType);
            return factory.build();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 加载多数据源配置
     */
    public void setEnvironment(Environment env) {
        initDefaultDataSource(env);
        initCustomDataSources(env);
    }

    /**
     * 初始化主数据源
     *
     * @author SHANHY
     * @create 2016年1月24日
     */
    public void initDefaultDataSource(Environment env) {
        // 读取主数据源
//        RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "datasource.");
//        Map<String, Object> prep = propertyResolver.getSubProperties("boot");
        Map<String, Object> dsMap = Maps.newHashMap();
        Properties prop = getYamlProp(DATASOURCE_RESOURCE);
        String url = prop.getProperty("datasource.boot.url");
        String username = prop.getProperty("datasource.boot.username");
        String password = prop.getProperty("datasource.boot.password");
        String driverClassName = prop.getProperty("datasource.boot.driverClassName");
        String type = prop.getProperty("datasource.boot.type");
        dsMap.put("url", url);
        dsMap.put("username", username);
        dsMap.put("password", password);
        dsMap.put("driverClassName", driverClassName);
        dsMap.put("type", type);

        defaultDataSource = buildDataSource(dsMap);
    }

    /**
     * 初始化更多数据源
     *
     * @author SHANHY
     * @create 2016年1月24日
     */
    @SuppressWarnings("unchecked")
    public void initCustomDataSources(Environment env) {
        // 读取配置文件获取更多数据源，也可以通过defaultDataSource读取数据库获取更多数据源
        //RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "datasource.");
        Map<String, Object> yamlMap = getYamlMap(DATASOURCE_RESOURCE);
        yamlMap = (Map<String, Object>) yamlMap.get("datasource");
        String dsPrefixs = (String) yamlMap.get("dynamic");
        for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源
            Map<String, Object> dsMap = (Map<String, Object>) yamlMap.get(dsPrefix);
            customDataSources.put(dsPrefix, buildDataSource(dsMap));
        }
    }

}